<!--
  - 建立TensorFlow模型
  - 可能是多段多分支的，所以用tf.model
 -->
 
<!-- 视图层 --><!-- 页头(上) -->
<template><header-comp />

<!-- TFjs初始化组件 -->
<tfjsInit />

<!-- 交互测试 -->
<uni-card title="交互测试">
  <!-- 通告栏 -->
  <uni-notice-bar text=
    "点击“运行代码”。"
  />

  <!-- 训练按钮容器及按钮 -->
  <view class="center"><button
    :disabled="!tfBackendRef"
    @click="testMain()"
    size="mini" type="primary"
  >
    运行代码
  </button></view>

  <!-- 打开图表板容器及按钮 -->
  <view class="center"><button
    @click="showVisor()"
    size="mini" type="primary"
  >
    打开图表板
  </button></view>

</uni-card>

<!-- 页脚(下) -->
<footer-comp /></template>

<!-- 逻辑层 -->
<script setup>
// 导入vue的onMounted、ref方法
import { shallowRef, ref } from "vue"
// 导入tfjs-visor库
import * as tf from "@tensorflow/tfjs"
import * as tfvis from "@tensorflow/tfjs-vis"
// 导入TF初始化组件
import tfjsInit from "../index/tfjs-init.vue"
// 导入xlsx库
// import * as XLSX from "xlsx"
// 导入公共方法库的方法
import {
  // tfjs计算后端挂载完毕的Ref对象
  tfBackendRef,
  // 显示图表板的方法
  showVisor,
  // 显示模型摘要的方法
  showModelSummary,
  // aoaTotfvisChart,
  // minToZero,
  // maxToOne
} from "../../scripts/app_common.js"
// 导入测试数据集
// import { exampleData } from "../../datasets/example-data.js"


/**
 * @ref对象
 */


/**
 * @其他对象
 */

// 主方法
function testMain() {

  /**
   * @输入层
   * 假设输入数据的形状为NHWC，即[批次，高度，宽度，通道数]
   * 假设输入的数据格式为：1200高，1600宽，3通道
   * （如果输入格式不是1200x1600，那么就）
   */



  /**
   * @输入层
   * 设定输入数据的形状为NHWC，即[批次，高度，宽度，通道数]
   * 固定输入的数据格式为：1600高，1200宽，3通道
   */
  const inputLayer = tf.input({
    name: "input",
    // 输入空间的形状，不需要加batchSize的null
    shape: [1600, 1200, 3],
    // batchSize维持默认（32）即可
  })

  /**
   * @支线1 卷积神经网络
   * 卷积核的数据形状为NHWC，即[批次，高度，宽度，通道数]
   * 假设输入的数据为
   */

  // reshape层，改变张量形状。以inputLayer层为输入
  const reshapeLayer = tf.layers.reshape({
    name: "cnn-reshape",
    // 输出空间的形状
    targetShape: [1600, 1],
  }).apply(inputLayer)

  // 卷积层1a。以reshapeLayer层为输入
  const conv1dLayer1a = tf.layers.conv1d({
    name: "cnn-conv1d-1a",
    // 滤波器数量，即输出的最后一层channels层增加倍数
    filters: 64,
    // 卷积核尺寸
    kernelSize: [5],
    // 卷积步长
    strides: 1,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
    // 不启用偏差
    useBias: false,
    // 激活函数
    activation: "softsign",
  }).apply(reshapeLayer)
  // 卷积层1b。以conv1dLayer1a层为输入
  const conv1dLayer1b = tf.layers.conv1d({
    name: "cnn-conv1d-1b",
    // 滤波器数量，即输出的最后一层channels层增加倍数
    filters: 64,
    // 卷积核尺寸
    kernelSize: [5],
    // 卷积步长
    strides: 1,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
    // 不启用偏差
    useBias: false,
    // 激活函数
    activation: "relu",
  }).apply(conv1dLayer1a)
  // 池化层-最大1。以conv1dLayer1b层为输入
  const maxPool1dLayer1 = tf.layers.maxPooling1d({
    name: "cnn-max-pool1d-1",
    // 池化尺寸
    poolSize: [5],
    // 池化步长
    strides: 5,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
  }).apply(conv1dLayer1b)

  // 卷积层2a。以maxPool1dLayer1层为输入
  const conv1dLayer2a = tf.layers.conv1d({
    name: "cnn-conv1d-2a",
    // 滤波器数量，即输出的最后一层channels层增加倍数
    filters: 64,
    // 卷积核尺寸
    kernelSize: [4],
    // 卷积步长
    strides: 1,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
    // 不启用偏差
    useBias: false,
    // 激活函数
    activation: "softsign",
  }).apply(maxPool1dLayer1)
  // 卷积层2b。以conv1dLayer2a层为输入
  const conv1dLayer2b = tf.layers.conv1d({
    name: "cnn-conv1d-2b",
    // 滤波器数量，即输出的最后一层channels层增加倍数
    filters: 64,
    // 卷积核尺寸
    kernelSize: [4],
    // 卷积步长
    strides: 1,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
    // 不启用偏差
    useBias: false,
    // 激活函数
    activation: "relu",
  }).apply(conv1dLayer2a)
  // 池化层-最大2。以conv1dLayer2b层为输入
  const maxPool1dLayer2 = tf.layers.maxPooling1d({
    name: "cnn-max-pool1d-2",
    // 池化尺寸
    poolSize: [4],
    // 池化步长
    strides: 4,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
  }).apply(conv1dLayer2b)

  // 卷积层3a。以maxPool1dLayer2层为输入
  const conv1dLayer3a = tf.layers.conv1d({
    name: "cnn-conv1d-3a",
    // 滤波器数量，即输出的最后一层channels层增加倍数
    filters: 64,
    // 卷积核尺寸
    kernelSize: [4],
    // 卷积步长
    strides: 1,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
    // 不启用偏差
    useBias: false,
    // 激活函数
    activation: "softsign",
  }).apply(maxPool1dLayer2)
  // 卷积层3b。以conv1dLayer3a层为输入
  const conv1dLayer3b = tf.layers.conv1d({
    name: "cnn-conv1d-3b",
    // 滤波器数量，即输出的最后一层channels层增加倍数
    filters: 64,
    // 卷积核尺寸
    kernelSize: [4],
    // 卷积步长
    strides: 1,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
    // 不启用偏差
    useBias: false,
    // 激活函数
    activation: "relu",
  }).apply(conv1dLayer3a)
  // 池化层-最大3。以conv1dLayer3b层为输入
  const maxPool1dLayer3 = tf.layers.maxPooling1d({
    name: "cnn-max-pool1d-3",
    // 池化尺寸
    poolSize: [4],
    // 池化步长
    strides: 4,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
  }).apply(conv1dLayer3b)

  // 卷积层4a。以maxPool1dLayer3层为输入
  const conv1dLayer4a = tf.layers.conv1d({
    name: "cnn-conv1d-4a",
    // 滤波器数量，即输出的最后一层channels层增加倍数
    filters: 64,
    // 卷积核尺寸
    kernelSize: [4],
    // 卷积步长
    strides: 1,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
    // 不启用偏差
    useBias: false,
    // 激活函数
    activation: "softsign",
  }).apply(maxPool1dLayer3)
  // 卷积层4b。以conv1dLayer4a层为输入
  const conv1dLayer4b = tf.layers.conv1d({
    name: "cnn-conv1d-4b",
    // 滤波器数量，即输出的最后一层channels层增加倍数
    filters: 64,
    // 卷积核尺寸
    kernelSize: [4],
    // 卷积步长
    strides: 1,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
    // 不启用偏差
    useBias: false,
    // 激活函数
    activation: "relu",
  }).apply(conv1dLayer4a)
  // 池化层-最大4。以conv1dLayer4b层为输入
  const maxPool1dLayer4 = tf.layers.maxPooling1d({
    name: "cnn-max-pool1d-4",
    // 池化尺寸
    poolSize: [4],
    // 池化步长
    strides: 4,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
  }).apply(conv1dLayer4b)

  // 卷积层5a。以maxPool1dLayer4层为输入
  const conv1dLayer5a = tf.layers.conv1d({
    name: "cnn-conv1d-5a",
    // 滤波器数量，即输出的最后一层channels层增加倍数
    filters: 64,
    // 卷积核尺寸
    kernelSize: [5],
    // 卷积步长
    strides: 1,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
    // 不启用偏差
    useBias: false,
    // 激活函数
    activation: "softsign",
  }).apply(maxPool1dLayer4)
  // 卷积层5b。以conv1dLayer5a层为输入
  const conv1dLayer5b = tf.layers.conv1d({
    name: "cnn-conv1d-5b",
    // 滤波器数量，即输出的最后一层channels层增加倍数
    filters: 64,
    // 卷积核尺寸
    kernelSize: [5],
    // 卷积步长
    strides: 1,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
    // 不启用偏差
    useBias: false,
    // 激活函数
    activation: "relu",
  }).apply(conv1dLayer5a)
  // 池化层-最大5。以conv1dLayer5b层为输入
  const maxPool1dLayer5 = tf.layers.maxPooling1d({
    name: "cnn-max-pool1d-5",
    // 池化尺寸
    poolSize: [5],
    // 池化步长
    strides: 5,
    // 边缘处理方法，valid为不添加填充，same为填充
    padding: "same",
  }).apply(conv1dLayer5b)

  // 扁平化层。以maxPool1dLayer5层为输入
  const flattenLayer = tf.layers.flatten({
    name: "cnn-flatten",
  }).apply(maxPool1dLayer5)
  // 全连接层。以flattenLayer层为输入
  const denseLayer1 = tf.layers.dense({
    name: "cnn-dense-1",
    // 输出空间的维度
    units: 64,
    // 启用偏差
    useBias: true,
    // 激活函数
    activation: "softsign",
  }).apply(flattenLayer)
  // dropout层。以denseLayer1层为输入
  const dropoutLayer = tf.layers.dropout({
    name: "cnn-dropout",
    // 随机“丢弃”一些前一层传来的神经元，以防过拟合
    rate: 0.5,
    // 掩码，以确保形状，一般不需要
    noiseShape: undefined,
    // 种子，相同的种子可以确保训练随机数能重复
    seed: 1,
  }).apply(denseLayer1)
  // 全连接层。以dropoutLayer层为输入
  const denseLayer2 = tf.layers.dense({
    name: "cnn-dense-2",
    // 输出空间的维度
    units: 2,
    // 启用偏差
    useBias: true,
    // 激活函数
    activation: "relu",
  }).apply(dropoutLayer)

  /**
   * @建立模型
   */
  const model = tf.model({
    name: "cnn-model",
    // 输入层
    inputs: inputLayer,
    // 输出层
    outputs: denseLayer2
  })

  /**
   * @编译模型
   */
  model.compile({
    // 优化器：Adam
    optimizer: tf.train.adam(),
    // 损失函数：均方误差
    loss: [tf.losses.meanSquaredError],
    // 评估指标：平均绝对误差
    metrics: [tf.metrics.meanAbsoluteError],
  })

  const indexByName = model.layers.findIndex((layer, index) => {
    if (layer.name === "cnn-conv1d-2b") {
      return index
    }
  })
  model.layers[indexByName].trainable = false


  // 打印模型结构
  console.log("模型的层结构：", model.layers)
  model.summary()
  showModelSummary(model, "模型结构")

  /**
   * @模型迁移
   */

  const outputLayer = model.getLayer("cnn-flatten")
  console.log("输出层：", outputLayer)

  // 构造截断模型
  const cutOffModel = tf.model({
    name: "cut-off-model",
    // 输入层
    inputs: model.inputs,
    // 输出层
    outputs: model.getLayer("cnn-flatten").output
  })
  // 可视化
  showModelSummary(cutOffModel, "截断模型结构")

  // 构造迁移模型
  // 新的全连接层。以原模型的flattenLayer层为输入
  const transferDenseLayer1 = tf.layers.dense({
    name: "transfer-dense-1",
    // 输出空间的维度
    units: 2,
    // 启用偏差
    useBias: true,
    // 激活函数
    activation: "softsign",
  }).apply(model.getLayer("cnn-flatten").output)
  // 迁移模型
  const transferModel = tf.model({
    name: "transfer-model",
    // 输入层
    inputs: model.inputs,
    // 输出层
    outputs: transferDenseLayer1
  })
  // 可视化
  showModelSummary(transferModel, "迁移模型结构")
  
  // 固化层。必须在编译模型之前，否则不生效
  // 按名称查找并固化层

  
  // // 固化多层：
  // for (let i = 1; i < 20; i++) {
  //   model.layers[i].trainable = false
  // }




}

</script>
